package com.sea.base.utils

import androidx.lifecycle.lifecycleScope
import com.sea.base.ext.global.getCurrentMillis
import com.sea.base.ext.global.removeGlobalRunnable
import com.sea.base.ext.global.toastAndLogDebug
import com.sea.base.ext.view.addNextOnVisibleChangedListener
import com.sea.base.ext.view.isVisibleToUser
import com.sea.base.ext.view.postMainDelayedLifecycle
import com.sea.base.ui.IUIContext
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.util.concurrent.CancellationException
import kotlin.math.max
import kotlin.math.min

/**
 * 带生命周期和缓存的轮询器
 * 根据轮询间隔自动轮询，不可见或destroy时自动停止，可见时自动开启
 * 开启轮询或者重置请调用[startPoll]
 * @param basePollIntervalMillis 基本轮询时间，每次轮询会逐渐增加时长，重新调用[startPoll]即可重置（当页面状态或数据变化时应重置）
 */
abstract class LifecyclePollCacheManager<BEAN : IPollCacheData>(
    val ui: com.sea.base.ui.IUIContext,
    val basePollIntervalMillis: Long,
) {

    private var job: Job? = null

    /**
     * 预计轮询的时间
     */
    private var estimatedPollMillis = 0L

    private var startRun: Runnable? = null
    private var currentMillisAdd = 0L

    val isStarting get() = job != null

    private var isFirstCall = true

    private fun initData() {
        if (isFirstCall) {
            isFirstCall = false
            ui.addNextOnVisibleChangedListener { isVisible ->
                if (isVisible) {
                    startPoll(min(basePollIntervalMillis, estimatedPollMillis - getCurrentMillis()))
                } else {
                    stopPoll()
                }
            }
        }
    }

    /**
     * 当你的数据变更时请再次调用，再次调用也会重置从[basePollIntervalMillis]开始
     */
    fun startPoll(delayMillis: Long) {
        stopPoll()
        estimatedPollMillis = getCurrentMillis() + max(0, delayMillis)
        initData()

        if (delayMillis > 0) {
            ui.postMainDelayedLifecycle(delayMillis, startRun ?: Runnable { startPoll(0) }.also { startRun = it })
            return
        }
        if (!ui.isVisibleToUser) return

        job = ui.lifecycleScope.launch {
            while (isActive) {
                val now = getCurrentMillis()
                val pollDatas = getPollDatas()
                if (pollDatas.isNotEmpty()) {
                    //清理超过时间的无用数据
                    val invalidDatas = pollDatas.filter { now - it.cacheMillis > basePollIntervalMillis }
                    if (invalidDatas.isNotEmpty()) {
                        //剩余的重新拉取
                        try {
                            onShouldRefresh(invalidDatas)
                            val now2 = getCurrentMillis()
                            invalidDatas.forEach {
                                it.cacheMillis = now2
                            }
                        } catch (e: Throwable) {
                            if (e !is CancellationException) {
                                "轮询查询报错：$e".toastAndLogDebug()
                                e.printStackTrace()
                            }
                        }
                    }
                }
                currentMillisAdd += 2000L
                val nexMillis = basePollIntervalMillis + currentMillisAdd
                estimatedPollMillis = getCurrentMillis() + nexMillis
                delay(nexMillis)
            }
        }
    }

    /**
     * 需要请求新数据
     * @return 返回变更的数据以便缓存
     */
    protected abstract suspend fun onShouldRefresh(pollInvalidDatas: List<BEAN>)

    protected abstract fun getPollDatas(): ImmutableList<BEAN>

    fun stopPoll() {
        job?.cancel()
        job = null
        startRun?.let {
            removeGlobalRunnable(it)
            startRun = null
        }
        currentMillisAdd = 0
    }
}

interface IPollCacheData {
    /**
     * 轮询时id标识，方便替换和变更
     */
    val pollDataId: Long

    /**
     * 缓存时间也就是创建时间，[getCurrentMillis]或者0
     */
    var cacheMillis: Long
}